home *** CD-ROM | disk | FTP | other *** search
- /* ImportAIFFSample.c */
- /*****************************************************************************/
- /* */
- /* Out Of Phase: Digital Music Synthesis on General Purpose Computers */
- /* Copyright (C) 1994 Thomas R. Lawrence */
- /* */
- /* This program is free software; you can redistribute it and/or modify */
- /* it under the terms of the GNU General Public License as published by */
- /* the Free Software Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will be useful, */
- /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- /* GNU General Public License for more details. */
- /* */
- /* You should have received a copy of the GNU General Public License */
- /* along with this program; if not, write to the Free Software */
- /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- /* */
- /* Thomas R. Lawrence can be reached at tomlaw@world.std.com. */
- /* */
- /*****************************************************************************/
-
- #include "MiscInfo.h"
- #include "Audit.h"
- #include "Debug.h"
- #include "Definitions.h"
-
- #include "ImportAIFFSample.h"
- #include "Memory.h"
- #include "MainWindowStuff.h"
- #include "Files.h"
- #include "Alert.h"
- #include "BufferedFileInput.h"
- #include "SampleConsts.h"
- #include "DataMunging.h"
- #include "SampleObject.h"
-
-
- /* prototype for the function that actually does the work. */
- static MyBoolean TryToImportAIFFFile(BufferedInputRec* File, NumBitsType* NumBitsOut,
- NumChannelsType* NumChannelsOut, char** RawDataPtrOut,
- long* NumSampleFramesOut, long* SamplingRateOut);
-
-
- /* this routine asks for a file and tries to import the contents of that */
- /* file as an AIFF sample. it reports any errors to the user. */
- void ImportAIFFSample(struct MainWindowRec* MainWindow)
- {
- FileSpec* WhereIsTheFile;
-
- CheckPtrExistence(MainWindow);
- WhereIsTheFile = GetFileAny();
- if (WhereIsTheFile != NIL)
- {
- FileType* FileDesc;
-
- if (OpenFile(WhereIsTheFile,&FileDesc,eReadOnly))
- {
- BufferedInputRec* File;
-
- File = NewBufferedInput(FileDesc);
- if (File != NIL)
- {
- NumBitsType NumBits;
- NumChannelsType NumChannels;
- char* SampleData;
- long NumFrames;
- long SamplingRate;
-
- if (TryToImportAIFFFile(File,&NumBits,&NumChannels,
- &SampleData,&NumFrames,&SamplingRate))
- {
- SampleObjectRec* ReturnedSampleObject;
-
- ReturnedSampleObject = MainWindowCopyRawSampleAndOpen(MainWindow,
- SampleData,NumBits,NumChannels,0,0,0,0,0,0,0,SamplingRate,
- 261.625565300598635);
- if (ReturnedSampleObject != NIL)
- {
- char* Filename;
-
- Filename = ExtractFileName(WhereIsTheFile);
- if (Filename != NIL)
- {
- /* we don't need to release the name */
- SampleObjectNewName(ReturnedSampleObject,Filename);
- }
- }
- ReleasePtr(SampleData);
- }
- EndBufferedInput(File);
- }
- else
- {
- AlertHalt("There is not enough memory available to import the file.",NIL);
- }
- CloseFile(FileDesc);
- }
- else
- {
- AlertHalt("Unable to open the file for reading.",NIL);
- }
- DisposeFileSpec(WhereIsTheFile);
- }
- }
-
-
- /* AIFF/AIFF-C File Format: */
- /* "FORM" */
- /* 4-byte big endian form chunk length descriptor (minus 8 for "FORM" & this) */
- /* 4-byte type */
- /* "AIFF" = AIFF format file */
- /* "AIFC" = AIFF-C format file */
- /* in any order, these chunks can occur: */
- /* Version Chunk (this only occurs in AIFF-C files) */
- /* "FVER" */
- /* 4-byte big endian length, which should always be the value 4 (four) */
- /* 4-byte date code. this is probably 0xA2805140 (stored big endian), but it */
- /* probably doesn't matter. */
- /* Common Chunk for AIFF files */
- /* "COMM" */
- /* 4-byte big endian length. */
- /* always 18 for AIFF files */
- /* 2-byte big endian number of channels */
- /* 4-byte big endian number of sample frames */
- /* 2-byte big endian number of bits per sample */
- /* a value in the domain 1..32 */
- /* 10-byte extended precision number of frames per second */
- /* Common Chunk for AIFF-C files */
- /* "COMM" */
- /* 4-byte big endian length. */
- /* 22 + compression method string length for AIFF-C files */
- /* 2-byte big endian number of channels */
- /* 4-byte big endian number of sample frames */
- /* 2-byte big endian number of bits per sample */
- /* a value in the domain 1..32 */
- /* 10-byte extended precision number of frames per second */
- /* 4-byte character code ID for the compression method */
- /* "NONE" means there is no compression method used */
- /* some characters in a string identifying the compression method */
- /* this must be padded to an even number of bytes, but the pad is */
- /* NOT included in the length descriptor for the chunk. */
- /* for uncompressed data, the string should be */
- /* "\x0enot compressed\x00", including the null, for 16 bytes. */
- /* the total chunk length is thus 38 bytes. */
- /* Sound Data Chunk */
- /* "SSND" */
- /* 4-byte big endian number of bytes in sample data array */
- /* 4-byte big endian offset to the first byte of sample data in the array */
- /* 4-byte big endian number of bytes to which the sound data is aligned. */
- /* any length vector of raw sound data. */
- /* this must be padded to an even number of bytes, but the pad is */
- /* NOT included in the length descriptor for the chunk. */
- /* Samples are stored in an integral number of bytes, the smallest that */
- /* is required for the specified number of bits. If this is not an even */
- /* multiple of 8, then the data is shifted left and the low bits are zeroed */
- /* Multichannel sound is interleaved with the left channel first. */
- static MyBoolean TryToImportAIFFFile(BufferedInputRec* File, NumBitsType* NumBitsOut,
- NumChannelsType* NumChannelsOut, char** RawDataPtrOut,
- long* NumSampleFramesOut, long* SamplingRateOut)
- {
- char CharBuff[4];
- MyBoolean IsAnAIFFCFile;
- long FormChunkLength;
- void* RawDataFromFile;
- MyBoolean RawDataFromFileIsValid = False;
- NumBitsType NumBits;
- MyBoolean NumBitsIsValid = False;
- NumChannelsType NumChannels;
- MyBoolean NumChannelsIsValid = False;
- long SamplingRate;
- MyBoolean SamplingRateIsValid = False;
- unsigned long NumSampleFrames;
- MyBoolean NumSampleFramesIsValid = False;
-
- CheckPtrExistence(File);
-
- /* "FORM" */
- if (!ReadBufferedInput(File,4,CharBuff))
- {
- DiskErrorPoint1:
- AlertHalt("Unable to read data from the file.",NIL);
- ExitTheProcedureErrorPoint:
- if (RawDataFromFileIsValid)
- {
- ReleasePtr((char*)RawDataFromFile);
- }
- return False;
- }
- if (!MemEqu(CharBuff,"FORM",4))
- {
- BadFilePoint1:
- AlertHalt("The file is not an AIFF or AIFF-C file.",NIL);
- goto ExitTheProcedureErrorPoint;
- }
-
- /* 4-byte big endian form chunk length descriptor (minus 8 for "FORM" & this) */
- if (!ReadBufferedUnsignedLongBigEndian(File,(unsigned long*)&FormChunkLength))
- {
- goto DiskErrorPoint1;
- }
-
- /* 4-byte type */
- /* "AIFF" = AIFF format file */
- /* "AIFC" = AIFF-C format file */
- if (!ReadBufferedInput(File,4,CharBuff))
- {
- goto DiskErrorPoint1;
- }
- if (MemEqu(CharBuff,"AIFF",4))
- {
- IsAnAIFFCFile = False;
- }
- else if (MemEqu(CharBuff,"AIFC",4))
- {
- IsAnAIFFCFile = True;
- }
- else
- {
- UnknownAIFFFilePoint:
- AlertHalt("The file is not a variant of AIFF or AIFF-C file that I can deal with.",NIL);
- goto ExitTheProcedureErrorPoint;
- }
- FormChunkLength -= 4;
-
- /* now, read in chunks until we die */
- while (FormChunkLength > 0)
- {
- long LocalChunkLength;
-
- /* get the chunk type */
- if (!ReadBufferedInput(File,4,CharBuff))
- {
- goto DiskErrorPoint1;
- }
- /* get the chunk length */
- if (!ReadBufferedUnsignedLongBigEndian(File,(unsigned long*)&LocalChunkLength))
- {
- goto DiskErrorPoint1;
- }
- FormChunkLength -= 8;
- /* adjust for even alignment */
- if ((LocalChunkLength % 2) != 0)
- {
- LocalChunkLength += 1;
- }
- FormChunkLength -= LocalChunkLength;
-
- /* decode the chunk */
- if (MemEqu(CharBuff,"COMM",4))
- {
- unsigned long Exponent;
- unsigned long Mantissa;
- char StupidExtendedThang[10];
-
- if (!IsAnAIFFCFile)
- {
- unsigned short ShortInteger;
-
- /* Common Chunk for AIFF files */
- /* "COMM" */
- /* 4-byte big endian length. */
- /* always 18 for AIFF files */
- if (LocalChunkLength != 18)
- {
- goto UnknownAIFFFilePoint;
- }
-
- /* 2-byte big endian number of channels */
- if (!ReadBufferedUnsignedShortBigEndian(File,&ShortInteger))
- {
- goto DiskErrorPoint1;
- }
- switch (ShortInteger)
- {
- default:
- goto UnknownAIFFFilePoint;
- case 1:
- NumChannels = eSampleMono;
- NumChannelsIsValid = True;
- break;
- case 2:
- NumChannels = eSampleStereo;
- NumChannelsIsValid = True;
- break;
- }
-
- /* 4-byte big endian number of sample frames */
- if (!ReadBufferedUnsignedLongBigEndian(File,&NumSampleFrames))
- {
- goto DiskErrorPoint1;
- }
- NumSampleFramesIsValid = True;
-
- /* 2-byte big endian number of bits per sample */
- /* a value in the domain 1..32 */
- if (!ReadBufferedUnsignedShortBigEndian(File,&ShortInteger))
- {
- goto DiskErrorPoint1;
- }
- switch (ShortInteger)
- {
- default:
- goto UnknownAIFFFilePoint;
- case 1: case 2: case 3: case 4:
- case 5: case 6: case 7: case 8:
- NumBits = eSample8bit;
- NumBitsIsValid = True;
- break;
- case 9: case 10: case 11: case 12:
- case 13: case 14: case 15: case 16:
- NumBits = eSample16bit;
- NumBitsIsValid = True;
- break;
- }
-
- /* 10-byte extended precision number of frames per second */
- if (!ReadBufferedInput(File,10,StupidExtendedThang))
- {
- goto DiskErrorPoint1;
- }
- }
- else
- {
- unsigned short ShortInteger;
- long Dumper;
-
- /* Common Chunk for AIFF-C files */
- /* "COMM" */
- /* 4-byte big endian length. */
- /* always 18 for AIFF files */
- if (LocalChunkLength < 22)
- {
- goto UnknownAIFFFilePoint;
- }
-
- /* 2-byte big endian number of channels */
- if (!ReadBufferedUnsignedShortBigEndian(File,&ShortInteger))
- {
- goto DiskErrorPoint1;
- }
- switch (ShortInteger)
- {
- default:
- goto UnknownAIFFFilePoint;
- case 1:
- NumChannels = eSampleMono;
- NumChannelsIsValid = True;
- break;
- case 2:
- NumChannels = eSampleStereo;
- NumChannelsIsValid = True;
- break;
- }
-
- /* 4-byte big endian number of sample frames */
- if (!ReadBufferedUnsignedLongBigEndian(File,&NumSampleFrames))
- {
- goto DiskErrorPoint1;
- }
- NumSampleFramesIsValid = True;
-
- /* 2-byte big endian number of bits per sample */
- /* a value in the domain 1..32 */
- if (!ReadBufferedUnsignedShortBigEndian(File,&ShortInteger))
- {
- goto DiskErrorPoint1;
- }
- switch (ShortInteger)
- {
- default:
- goto UnknownAIFFFilePoint;
- case 1: case 2: case 3: case 4:
- case 5: case 6: case 7: case 8:
- NumBits = eSample8bit;
- NumBitsIsValid = True;
- break;
- case 9: case 10: case 11: case 12:
- case 13: case 14: case 15: case 16:
- NumBits = eSample16bit;
- NumBitsIsValid = True;
- break;
- }
-
- /* 10-byte extended precision number of frames per second */
- if (!ReadBufferedInput(File,10,StupidExtendedThang))
- {
- goto DiskErrorPoint1;
- }
-
- /* 4-byte character code ID for the compression method */
- /* "NONE" means there is no compression method used */
- if (!ReadBufferedInput(File,4,CharBuff))
- {
- goto DiskErrorPoint1;
- }
- if (!MemEqu(CharBuff,"NONE",4))
- {
- goto UnknownAIFFFilePoint;
- }
-
- /* some characters in a string identifying the compression method */
- /* this must be padded to an even number of bytes, but the pad is */
- /* NOT included in the length descriptor for the chunk. */
- /* for uncompressed data, the string should be */
- /* "\x0enot compressed\x00", including the null, for 16 bytes. */
- /* the total chunk length is thus 38 bytes. */
- for (Dumper = 0; Dumper < LocalChunkLength - 22; Dumper += 1)
- {
- unsigned char Stupid;
-
- if (!ReadBufferedUnsignedChar(File,&Stupid))
- {
- goto DiskErrorPoint1;
- }
- }
- }
-
- /* extended 22050 = 400D AC44000000000000 */
- /* extended 22051 = 400D AC46000000000000 */
- /* extended 44100 = 400E AC44000000000000 */
- /* extended 44101 = 400E AC45000000000000 */
- Exponent = (((long)StupidExtendedThang[0] & 0xff) << 8)
- | ((long)StupidExtendedThang[1] & 0xff);
- Mantissa = (((long)StupidExtendedThang[2] & 0xff) << 24)
- | (((long)StupidExtendedThang[3] & 0xff) << 16)
- | (((long)StupidExtendedThang[4] & 0xff) << 8)
- | ((long)StupidExtendedThang[5] & 0xff);
- SamplingRate = (Mantissa >> (0x401e - Exponent));
- if (SamplingRate < MINSAMPLINGRATE)
- {
- SamplingRate = MINSAMPLINGRATE;
- }
- if (SamplingRate > MAXSAMPLINGRATE)
- {
- SamplingRate = MAXSAMPLINGRATE;
- }
- SamplingRateIsValid = True;
- }
- else if (MemEqu(CharBuff,"SSND",4))
- {
- unsigned long AlignmentFactor;
- unsigned long OffsetToFirstByte;
-
- /* Sound Data Chunk */
- /* "SSND" */
- /* 4-byte big endian number of bytes in sample data array */
-
- /* only one of these is allowed */
- if (RawDataFromFileIsValid)
- {
- goto UnknownAIFFFilePoint;
- }
-
- /* 4-byte big endian offset to the first byte of sample data in the array */
- if (!ReadBufferedUnsignedLongBigEndian(File,&OffsetToFirstByte))
- {
- goto DiskErrorPoint1;
- }
- if (OffsetToFirstByte != 0)
- {
- goto UnknownAIFFFilePoint;
- }
-
- /* 4-byte big endian number of bytes to which the sound data is aligned. */
- if (!ReadBufferedUnsignedLongBigEndian(File,&AlignmentFactor))
- {
- goto DiskErrorPoint1;
- }
- if (AlignmentFactor != 0)
- {
- goto UnknownAIFFFilePoint;
- }
-
- /* any length vector of raw sound data. */
- /* this must be padded to an even number of bytes, but the pad is */
- /* NOT included in the length descriptor for the chunk. */
- /* Samples are stored in an integral number of bytes, the smallest that */
- /* is required for the specified number of bits. If this is not an even */
- /* multiple of 8, then the data is shifted left and the low bits are zeroed */
- /* Multichannel sound is interleaved with the left channel first. */
- RawDataFromFile = AllocPtrCanFail(LocalChunkLength - 8,
- "TryToImportAIFFFile: RawDataBuffer");
- if (RawDataFromFile == NIL)
- {
- NotEnoughMemoryPoint:
- AlertHalt("There is not enough memory available to import the sample.",NIL);
- goto ExitTheProcedureErrorPoint;
- }
- RawDataFromFileIsValid = True;
- if (!ReadBufferedInput(File,LocalChunkLength - 8,(char*)RawDataFromFile))
- {
- goto DiskErrorPoint1;
- }
- }
- else
- {
- /* just read the data & get rid of it */
- while (LocalChunkLength > 0)
- {
- unsigned char Stupid;
-
- if (!ReadBufferedUnsignedChar(File,&Stupid))
- {
- goto DiskErrorPoint1;
- }
- LocalChunkLength -= 1;
- }
- }
- }
-
- /* now, deal with the stuff we just got */
- if (!RawDataFromFileIsValid || !NumBitsIsValid || !NumChannelsIsValid
- || !SamplingRateIsValid || !NumSampleFramesIsValid)
- {
- goto UnknownAIFFFilePoint;
- }
-
- switch (NumChannels)
- {
- default:
- EXECUTE(PRERR(ForceAbort,"ImportAIFFSample: bad NumChannels"));
- break;
- case eSampleMono:
- switch (NumBits)
- {
- default:
- EXECUTE(PRERR(ForceAbort,"ImportAIFFSample: bad NumBits"));
- break;
- case eSample8bit:
- {
- long Scan;
- long ActualLimit;
- char* SampleVector;
-
- SampleVector = AllocPtrCanFail(sizeof(char) * NumSampleFrames,
- "ImportAIFFSample: finished vector");
- ActualLimit = PtrSize((char*)RawDataFromFile);
- for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
- {
- PRNGCHK(SampleVector,&(SampleVector[Scan]),
- sizeof(SampleVector[Scan]));
- if (Scan * sizeof(char) < ActualLimit)
- {
- PRNGCHK(RawDataFromFile,&(((signed char*)RawDataFromFile)[Scan]),
- sizeof(((signed char*)RawDataFromFile)[Scan]));
- SampleVector[Scan] = ((signed char*)RawDataFromFile)[Scan];
- }
- else
- {
- SampleVector[Scan] = 0;
- }
- }
- *RawDataPtrOut = (char*)SampleVector;
- }
- break;
- case eSample16bit:
- {
- long Scan;
- long ActualLimit;
- short* SampleVector;
-
- SampleVector = (short*)AllocPtrCanFail(sizeof(short) * NumSampleFrames,
- "ImportAIFFSample: finished vector");
- ActualLimit = PtrSize((char*)RawDataFromFile) / 2;
- for (Scan = 0; Scan < NumSampleFrames; Scan += 1)
- {
- PRNGCHK(SampleVector,&(SampleVector[Scan]),
- sizeof(SampleVector[Scan]));
- if (Scan * sizeof(char) < ActualLimit)
- {
- PRNGCHK(RawDataFromFile,&(((short*)RawDataFromFile)[Scan * 2 + 1]),
- sizeof(((char*)RawDataFromFile)[Scan * 2 + 1]));
- SampleVector[Scan]
- = (((short)((signed char*)RawDataFromFile)[2
- * Scan] & 0xff) << 8) | (((char*)RawDataFromFile)[2
- * Scan + 1] & 0xff);
- }
- else
- {
- SampleVector[Scan] = 0;
- }
- }
- *RawDataPtrOut = (char*)SampleVector;
- }
- break;
- }
- break;
- case eSampleStereo:
- switch (NumBits)
- {
- default:
- EXECUTE(PRERR(ForceAbort,"ImportAIFFSample: bad NumBits"));
- break;
- case eSample8bit:
- {
- long Scan;
- long ActualLimit;
- char* SampleVector;
-
- SampleVector = AllocPtrCanFail(sizeof(char) * NumSampleFrames * 2,
- "ImportAIFFSample: finished vector");
- ActualLimit = PtrSize((char*)RawDataFromFile);
- for (Scan = 0; Scan < NumSampleFrames * 2; Scan += 1)
- {
- PRNGCHK(SampleVector,&(SampleVector[Scan]),
- sizeof(SampleVector[Scan]));
- if (Scan * sizeof(char) < ActualLimit)
- {
- PRNGCHK(RawDataFromFile,&(((signed char*)RawDataFromFile)[Scan]),
- sizeof(((signed char*)RawDataFromFile)[Scan]));
- SampleVector[Scan] = ((signed char*)RawDataFromFile)[Scan];
- }
- else
- {
- SampleVector[Scan] = 0;
- }
- }
- *RawDataPtrOut = (char*)SampleVector;
- }
- break;
- case eSample16bit:
- {
- long Scan;
- long ActualLimit;
- short* SampleVector;
-
- SampleVector = (short*)AllocPtrCanFail(sizeof(short) * 2
- * NumSampleFrames,"ImportAIFFSample: finished vector");
- ActualLimit = PtrSize((char*)RawDataFromFile) / 2;
- for (Scan = 0; Scan < NumSampleFrames * 2; Scan += 1)
- {
- PRNGCHK(SampleVector,&(SampleVector[Scan]),
- sizeof(SampleVector[Scan]));
- if (Scan * sizeof(char) < ActualLimit)
- {
- PRNGCHK(RawDataFromFile,&(((char*)RawDataFromFile)[Scan * 2
- + 1]),sizeof(((char*)RawDataFromFile)[Scan * 2 + 1]));
- SampleVector[Scan]
- = (((short)((signed char*)RawDataFromFile)[2
- * Scan] & 0xff) << 8) | (((char*)RawDataFromFile)[2
- * Scan + 1] & 0xff);
- }
- else
- {
- SampleVector[Scan] = 0;
- }
- }
- *RawDataPtrOut = (char*)SampleVector;
- }
- break;
- }
- break;
- }
-
- ReleasePtr((char*)RawDataFromFile);
-
- *NumBitsOut = NumBits;
- *NumChannelsOut = NumChannels;
- CheckPtrExistence(*RawDataPtrOut);
- *NumSampleFramesOut = NumSampleFrames;
- *SamplingRateOut = SamplingRate;
-
- return True;
- }
-